#ifndef CIRCBUF_H_
#define CIRCBUF_H_

#include <stdint.h>
#include <sys/types.h>

typedef struct {
        uint8_t *buf;
        int head;
        int tail;
        int size;
}circbuf_t;

#define DECLARE_STATIC_CIRC_BUF(s, size) \
        static uint8_t _circ_buf_##s[size];\
        static circbuf_t s = { \
                 _circ_buf_##s, \
                0, \
                0, \
                size \
        }

/* Return count in buffer.  */
#define _CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1))

/* Return space available, 0..size-1.  We always leave one free char
   as a completely full buffer has head == tail, which is the same as
   empty.  */
#define _CIRC_SPACE(head,tail,size) _CIRC_CNT((tail),((head)+1),(size))


/* Return count up to the end of the buffer.  Carefully avoid
   accessing head and tail more than once, so they can change
   underneath us without returning inconsistent results.  */
#define _CIRC_CNT_TO_END(head,tail,size) \
        ({int end = (size) - (tail); \
          int n = ((head) + end) & ((size)-1); \
          n < end ? n : end;})

/* Return space available up to the end of the buffer.  */
#define _CIRC_SPACE_TO_END(head,tail,size) \
        ({int end = (size) - 1 - (head); \
          int n = (end + (tail)) & ((size)-1); \
          n <= end ? n : end+1;})

/* circ buf helpers */
#define CIRC_SIZE(s)            (s)->size
#define CIRC_POS(s)             (s)->tail
#define CIRC_HEAD(s)            (s)->head
#define CIRC_ELM(s, idx)        (s)->buf[idx]

#define CIRC_CNT(s)             _CIRC_CNT((s)->head, (s)->tail, (s)->size)
#define CIRC_EMPTY(s)           ((s)->head == (s)->tail)
#define CIRC_CLEAR(s)           do { (s)->head = (s)->tail = 0; } while(0)
#define CIRC_SPACE(s)           _CIRC_SPACE((s)->head, (s)->tail, (s)->size)
#define CIRC_CNT_TO_END(s)      _CIRC_CNT_TO_END((s)->head, (s)->tail, (s)->size)
#define CIRC_SPACE_TO_END(s)    _CIRC_SPACE_TO_END((s)->head, (s)->tail, (s)->size)

/* get one element from buffer */
#define CIRC_GET(s)                     (s)->buf[(s)->tail]
#define CIRC_GET_TIDY_N(s, n)           (s)->tail = (((s)->tail + n) & ((s)->size - 1))
#define CIRC_GET_TIDY(s)                (s)->tail = (((s)->tail + 1) & ((s)->size - 1))
#define CIRC_GET_HEAD(s)                (s)->buf[((s)->tail + CIRC_CNT(s) - 1) & ((s)->size - 1)]

/* put one element to buffer */
#define CIRC_PUT(s, c)                  (s)->buf[(s)->head] = c
#define CIRC_PUT_TIDY_N(s, n)           (s)->head = (((s)->head + n) & ((s)->size - 1))
#define CIRC_PUT_TIDY(s)                (s)->head = (((s)->head + 1) & ((s)->size - 1))

int circ_buf_get(circbuf_t *cb, uint8_t *buf, int count);
int circ_buf_put(circbuf_t *cb, const uint8_t *buf, int count);

#endif /* CIRCBUF_H_ */
